--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
meshchatx/src/backend/rnode_support.py dff83e198e78b84ec225e53ce3ab119f598b0a01 (dff83e19) Text, 12.77 KB
T8b949e# SPDX-License-Identifier: 0BSD
T8b949e"""RNode USB serial / Bluetooth / BLE support checks for desktop and Android.
RNode over TCP ("RNode over IP") needs no native Android modules and works
unconditionally. On Android, MeshChatX ships a Chaquopy jnius shim plus a
patched usb4a context bridge so USB serial and classic Bluetooth can use
usbserial4a. BLE (ble://) uses the bundled able package and org.able.BLE.
Serial and classic-Bluetooth still need usbserial4a + jnius. BLE needs able.
This module lets the rest of the app tell which RNode config entries can
actually be brought up on the current build, so only the genuinely
unsupported ones get disabled.
"""
Tff7b72from T7ee787__future__ Tff7b72import Te6edf3annotations
Tff7b72import T7ee787importlibT7ee787.T7ee787util
Tff7b72import T7ee787logging
Te6edf3logger Tff7b72= Te6edf3loggingTff7b72.Td2a8ffgetLoggerTb4b4b4(Tff7b72__name__Tb4b4b4)
Te6edf3_TRUE_STRINGS Tff7b72= Tb4b4b4(Ta5d6ff"Ta5d6fftrueTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffyesTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff1Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffonTa5d6ff"Tb4b4b4)
Tff7b72def Td2a8ff_optional_module_availableTb4b4b4(Te6edf3module_nameTb4b4b4: Tffa657strTb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
T8b949e"""Return True when *module_name* can be resolved without importing it."""
Tff7b72tryTb4b4b4:
Tff7b72return Te6edf3importlibTff7b72.Td2a8ffutilTff7b72.Td2a8fffind_specTb4b4b4(Te6edf3module_nameTb4b4b4) Tff7b72is Tff7b72not Tff7b72None
Tff7b72except Tb4b4b4(Tf85149ImportErrorTb4b4b4, Tf85149ModuleNotFoundErrorTb4b4b4, Tf85149ValueErrorTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72False
Tff7b72def Td2a8ff_is_chaquopy_androidTb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
Tff7b72tryTb4b4b4:
Tff7b72from T7ee787meshchatxT7ee787.T7ee787android_push_bridge Tff7b72import Te6edf3_is_chaquopy_android Tff7b72as Te6edf3_check
Tff7b72return Te6edf3_checkTb4b4b4(Tb4b4b4)
Tff7b72except Tf85149ImportErrorTb4b4b4:
Tff7b72return Tff7b72False
Tff7b72def Td2a8ffandroid_usbserial4a_availableTb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
T8b949e"""True when usbserial4a can be imported (RNode serial/Bluetooth-classic on Android)."""
Tff7b72return Te6edf3_optional_module_availableTb4b4b4(Ta5d6ff"Ta5d6ffusbserial4aTa5d6ff"Tb4b4b4)
Tff7b72def Td2a8ffandroid_jnius_availableTb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
T8b949e"""True when jnius (or the Chaquopy jnius shim) can be imported.
RNS's Android-specific RNodeInterface needs jnius for USB serial and
classic Bluetooth (RFCOMM) access. MeshChatX ships a Chaquopy-backed
shim under android/app/src/main/python/jnius so this resolves on device.
"""
Tff7b72return Te6edf3_optional_module_availableTb4b4b4(Ta5d6ff"Ta5d6ffjniusTa5d6ff"Tb4b4b4)
Tff7b72def Td2a8ffandroid_able_availableTb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
T8b949e"""True when able can be imported (BLE GATT support for RNode ble://).
MeshChatX ships a Chaquopy-compatible able package plus org.able.BLE.
"""
Tff7b72return Te6edf3_optional_module_availableTb4b4b4(Ta5d6ff"Ta5d6ffableTa5d6ff"Tb4b4b4)
Tff7b72def Td2a8ffdesktop_serial_stack_availableTb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
Tff7b72return Te6edf3_optional_module_availableTb4b4b4(Ta5d6ff"Ta5d6ffserial.tools.list_portsTa5d6ff"Tb4b4b4)
Tff7b72def Td2a8ffdesktop_ble_stack_availableTb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
Tff7b72return Te6edf3_optional_module_availableTb4b4b4(Ta5d6ff"Ta5d6ffbleakTa5d6ff"Tb4b4b4)
Tff7b72def Td2a8ff_is_rnode_tcp_config_typeTb4b4b4(Te6edf3iface_typeTb4b4b4: Tffa657objectTb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
Tff7b72return Te6edf3iface_type Tff7b72in Tb4b4b4(Ta5d6ff"Ta5d6ffRNodeInterfaceTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffRNodeIPInterfaceTa5d6ff"Tb4b4b4)
Tff7b72def Td2a8ffrnode_serial_supportedTb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
T8b949e"""Whether RNode USB serial / classic-Bluetooth ports can be opened here.
This does not cover RNode over TCP (always supported) or ble:// (covered
by android_able_available() on Android).
"""
Tff7b72if Te6edf3_is_chaquopy_androidTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3android_usbserial4a_availableTb4b4b4(Tb4b4b4) Tff7b72and Te6edf3android_jnius_availableTb4b4b4(Tb4b4b4)
Tff7b72return Te6edf3desktop_serial_stack_availableTb4b4b4(Tb4b4b4)
Tff7b72def Td2a8ff_is_enabledTb4b4b4(Te6edf3ifaceTb4b4b4: Tffa657dictTb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
Tff7b72for Te6edf3key Tff7b72in Tb4b4b4(Ta5d6ff"Ta5d6ffinterface_enabledTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffenabledTa5d6ff"Tb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3key Tff7b72in Te6edf3iface Tff7b72and Tffa657strTb4b4b4(Te6edf3ifaceTff7b72.Td2a8ffgetTb4b4b4(Te6edf3keyTb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4)Tb4b4b4)Tff7b72.Td2a8fflowerTb4b4b4(Tb4b4b4) Tff7b72in Te6edf3_TRUE_STRINGSTb4b4b4:
Tff7b72return Tff7b72True
Tff7b72return Tff7b72False
Tff7b72def Td2a8ffrnode_port_is_tcpTb4b4b4(Te6edf3portTb4b4b4: Tffa657objectTb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
Tff7b72return Tffa657strTb4b4b4(Te6edf3port Tff7b72or Ta5d6ff"Ta5d6ff"Tb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)Tff7b72.Td2a8fflowerTb4b4b4(Tb4b4b4)Tff7b72.Td2a8ffstartswithTb4b4b4(Ta5d6ff"Ta5d6fftcp://Ta5d6ff"Tb4b4b4)
Tff7b72def Td2a8ffrnode_port_is_bleTb4b4b4(Te6edf3portTb4b4b4: Tffa657objectTb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
Tff7b72return Tffa657strTb4b4b4(Te6edf3port Tff7b72or Ta5d6ff"Ta5d6ff"Tb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)Tff7b72.Td2a8fflowerTb4b4b4(Tb4b4b4)Tff7b72.Td2a8ffstartswithTb4b4b4(Ta5d6ff"Ta5d6ffble://Ta5d6ff"Tb4b4b4)
Tff7b72def Td2a8ff_tcp_host_from_portTb4b4b4(Te6edf3portTb4b4b4: Tffa657objectTb4b4b4) Tff7b72-Tff7b72> Tffa657str Tff7b72| Tff7b72NoneTb4b4b4:
Tff7b72if Tff7b72not Te6edf3rnode_port_is_tcpTb4b4b4(Te6edf3portTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72None
Te6edf3host_part Tff7b72= Tffa657strTb4b4b4(Te6edf3portTb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)Tb4b4b4[Tffa657lenTb4b4b4(Ta5d6ff"Ta5d6fftcp://Ta5d6ff"Tb4b4b4) Tb4b4b4:Tb4b4b4]Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Ta5d6ff"Ta5d6ff:Ta5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3host_part Tff7b72or Tffa657setTb4b4b4(Te6edf3host_partTb4b4b4) Tff7b72<Tff7b72= Tb4b4b4{Ta5d6ff"Ta5d6ff/Ta5d6ff"Tb4b4b4}Tb4b4b4:
Tff7b72return Tff7b72None
Tff7b72return Te6edf3host_part
Tff7b72def Td2a8ff_port_is_blankTb4b4b4(Te6edf3portTb4b4b4: Tffa657objectTb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
Tff7b72return Tff7b72not Tffa657strTb4b4b4(Te6edf3port Tff7b72or Ta5d6ff"Ta5d6ff"Tb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)
Tff7b72def Td2a8ff_rnode_iface_transportTb4b4b4(Te6edf3ifaceTb4b4b4: Tffa657dictTb4b4b4) Tff7b72-Tff7b72> Tffa657strTb4b4b4:
T8b949e"""Classify an RNodeInterface config entry's transport.
Returns one of "tcp", "ble", "bluetooth_classic", or "serial".
"""
Te6edf3port Tff7b72= Te6edf3ifaceTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffportTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3rnode_port_is_tcpTb4b4b4(Te6edf3portTb4b4b4)Tb4b4b4:
Tff7b72return Ta5d6ff"Ta5d6fftcpTa5d6ff"
Tff7b72if Te6edf3rnode_port_is_bleTb4b4b4(Te6edf3portTb4b4b4)Tb4b4b4:
Tff7b72return Ta5d6ff"Ta5d6ffbleTa5d6ff"
Te6edf3allow_bluetooth Tff7b72= Tffa657strTb4b4b4(Te6edf3ifaceTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffallow_bluetoothTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4)Tb4b4b4)Tff7b72.Td2a8fflowerTb4b4b4(Tb4b4b4) Tff7b72in Te6edf3_TRUE_STRINGS
Tff7b72if Te6edf3_port_is_blankTb4b4b4(Te6edf3portTb4b4b4) Tff7b72and Te6edf3allow_bluetoothTb4b4b4:
Tff7b72return Ta5d6ff"Ta5d6ffbluetooth_classicTa5d6ff"
Tff7b72return Ta5d6ff"Ta5d6ffserialTa5d6ff"
Tff7b72def Td2a8ffrnode_transport_supportedTb4b4b4(Te6edf3ifaceTb4b4b4: Tffa657dictTb4b4b4, Tff7b72*Tb4b4b4, Te6edf3is_androidTb4b4b4: Tffa657bool Tff7b72| Tff7b72None Tff7b72= Tff7b72NoneTb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
T8b949e"""Whether a specific RNodeInterface config entry can be brought up here.
RNode over TCP always works. Serial and classic-Bluetooth need
usbserial4a + jnius on Android. BLE needs able on Android. On desktop,
serial and classic-Bluetooth rely on pyserial; BLE relies on bleak.
is_android lets a caller that already determined the platform pass
that result through explicitly, instead of re-detecting it here.
"""
Tff7b72if Te6edf3is_android Tff7b72is Tff7b72NoneTb4b4b4:
Te6edf3is_android Tff7b72= Te6edf3_is_chaquopy_androidTb4b4b4(Tb4b4b4)
Te6edf3transport Tff7b72= Te6edf3_rnode_iface_transportTb4b4b4(Te6edf3ifaceTb4b4b4)
Tff7b72if Te6edf3transport Tff7b72== Ta5d6ff"Ta5d6fftcpTa5d6ff"Tb4b4b4:
Tff7b72return Tff7b72True
Tff7b72if Te6edf3is_androidTb4b4b4:
Tff7b72if Te6edf3transport Tff7b72== Ta5d6ff"Ta5d6ffbleTa5d6ff"Tb4b4b4:
Tff7b72return Te6edf3android_able_availableTb4b4b4(Tb4b4b4)
Tff7b72return Te6edf3android_usbserial4a_availableTb4b4b4(Tb4b4b4) Tff7b72and Te6edf3android_jnius_availableTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3transport Tff7b72== Ta5d6ff"Ta5d6ffbleTa5d6ff"Tb4b4b4:
Tff7b72return Te6edf3desktop_ble_stack_availableTb4b4b4(Tb4b4b4)
Tff7b72return Te6edf3desktop_serial_stack_availableTb4b4b4(Tb4b4b4)
Tff7b72def Td2a8ffnormalize_rnode_tcp_host_in_configTb4b4b4(Te6edf3config_pathTb4b4b4: Tffa657strTb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
T8b949e"""Backfill tcp_host for RNodeInterface entries configured with a tcp:// port.
RNS's desktop RNodeInterface derives tcp_host from a tcp:// port itself,
but the Android-specific implementation reads tcp_host as its own,
separate config key and never looks at port for that. Configs written or
hand-edited with only port = tcp://host:port therefore silently try
(and fail) to open the RNode as a serial device on Android. This keeps
both keys in sync regardless of how the entry was created, so RNode over
TCP works the same way on both platforms.
Returns True if any interfaces were modified.
"""
Tff7b72import T7ee787os
Tff7b72if Tff7b72not Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffisfileTb4b4b4(Te6edf3config_pathTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72False
Tff7b72tryTb4b4b4:
Tff7b72from T7ee787RNST7ee787.T7ee787vendorT7ee787.T7ee787configobj Tff7b72import Te6edf3ConfigObj
Te6edf3cfg Tff7b72= Te6edf3ConfigObjTb4b4b4(Te6edf3config_pathTb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72return Tff7b72False
Te6edf3modified Tff7b72= Tff7b72False
Te6edf3interfaces Tff7b72= Te6edf3cfgTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffinterfacesTa5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3interfacesTb4b4b4, Tffa657dictTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72False
Tff7b72for Te6edf3_iface_nameTb4b4b4, Te6edf3iface Tff7b72in Te6edf3interfacesTff7b72.Td2a8ffitemsTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3ifaceTb4b4b4, Tffa657dictTb4b4b4)Tb4b4b4:
Tff7b72continue
Tff7b72if Tff7b72not Te6edf3_is_rnode_tcp_config_typeTb4b4b4(Te6edf3ifaceTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4)Tb4b4b4)Tb4b4b4:
Tff7b72continue
Te6edf3port Tff7b72= Te6edf3ifaceTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffportTa5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3rnode_port_is_tcpTb4b4b4(Te6edf3portTb4b4b4)Tb4b4b4:
Tff7b72continue
Te6edf3host_part Tff7b72= Te6edf3_tcp_host_from_portTb4b4b4(Te6edf3portTb4b4b4)
Tff7b72if Te6edf3host_part Tff7b72is Tff7b72NoneTb4b4b4:
Tff7b72continue
Tff7b72if Tffa657strTb4b4b4(Te6edf3ifaceTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftcp_hostTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4)Tb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4) Tff7b72!= Te6edf3host_partTb4b4b4:
Te6edf3ifaceTb4b4b4[Ta5d6ff"Ta5d6fftcp_hostTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3host_part
Te6edf3modified Tff7b72= Tff7b72True
Tff7b72if Te6edf3modifiedTb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3cfgTff7b72.Td2a8ffwriteTb4b4b4(Tb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72pass
Tff7b72return Te6edf3modified
Tff7b72def Td2a8ffdisable_rnode_interfaces_in_configTb4b4b4(
Te6edf3config_pathTb4b4b4: Tffa657strTb4b4b4,
Tff7b72*Tb4b4b4,
Te6edf3is_androidTb4b4b4: Tffa657bool Tff7b72| Tff7b72None Tff7b72= Tff7b72NoneTb4b4b4,
Tb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
T8b949e"""Disable RNode* interfaces in a Reticulum config file that can't run here.
RNode over TCP is left enabled since it needs no native Android modules.
RNodeMultiInterface has no Android-specific implementation upstream and is
always disabled on Android. Serial, classic-Bluetooth, and BLE entries are
disabled only when their required native module isn't available.
Returns True if any interfaces were disabled.
"""
Tff7b72import T7ee787os
Tff7b72if Tff7b72not Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffisfileTb4b4b4(Te6edf3config_pathTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72False
Tff7b72tryTb4b4b4:
Tff7b72from T7ee787RNST7ee787.T7ee787vendorT7ee787.T7ee787configobj Tff7b72import Te6edf3ConfigObj
Te6edf3cfg Tff7b72= Te6edf3ConfigObjTb4b4b4(Te6edf3config_pathTb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72return Tff7b72False
Tff7b72if Te6edf3is_android Tff7b72is Tff7b72NoneTb4b4b4:
Te6edf3is_android Tff7b72= Te6edf3_is_chaquopy_androidTb4b4b4(Tb4b4b4)
Te6edf3modified Tff7b72= Tff7b72False
Te6edf3interfaces Tff7b72= Te6edf3cfgTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffinterfacesTa5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3interfacesTb4b4b4, Tffa657dictTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72False
Tff7b72for Te6edf3_iface_nameTb4b4b4, Te6edf3iface Tff7b72in Te6edf3interfacesTff7b72.Td2a8ffitemsTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3ifaceTb4b4b4, Tffa657dictTb4b4b4)Tb4b4b4:
Tff7b72continue
Te6edf3iface_type Tff7b72= Te6edf3ifaceTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3iface_typeTb4b4b4, Tffa657strTb4b4b4) Tff7b72or Tff7b72not Te6edf3iface_typeTff7b72.Td2a8ffstartswithTb4b4b4(Ta5d6ff"Ta5d6ffRNodeTa5d6ff"Tb4b4b4)Tb4b4b4:
Tff7b72continue
Tff7b72if Tff7b72not Te6edf3_is_enabledTb4b4b4(Te6edf3ifaceTb4b4b4)Tb4b4b4:
Tff7b72continue
Tff7b72if Te6edf3iface_type Tff7b72== Ta5d6ff"Ta5d6ffRNodeMultiInterfaceTa5d6ff"Tb4b4b4:
Te6edf3should_disable Tff7b72= Te6edf3is_android
Tff7b72elseTb4b4b4:
Te6edf3should_disable Tff7b72= Tff7b72not Te6edf3rnode_transport_supportedTb4b4b4(Te6edf3ifaceTb4b4b4, Te6edf3is_androidTff7b72=Te6edf3is_androidTb4b4b4)
Tff7b72if Te6edf3should_disableTb4b4b4:
Te6edf3ifaceTb4b4b4[Ta5d6ff"Ta5d6ffinterface_enabledTa5d6ff"Tb4b4b4] Tff7b72= Ta5d6ff"Ta5d6fffalseTa5d6ff"
Te6edf3modified Tff7b72= Tff7b72True
Tff7b72if Te6edf3modifiedTb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3cfgTff7b72.Td2a8ffwriteTb4b4b4(Tb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72pass
Tff7b72return Te6edf3modified
Tff7b72def Td2a8ff_find_invalid_rnode_txpowerTb4b4b4(
Te6edf3iface_nameTb4b4b4: Tffa657strTb4b4b4,
Te6edf3ifaceTb4b4b4: Tffa657dictTb4b4b4,
Te6edf3interfacesTb4b4b4: Tffa657dictTb4b4b4,
Tb4b4b4) Tff7b72-Tff7b72> Tffa657object Tff7b72| Tff7b72NoneTb4b4b4:
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787interface_editor Tff7b72import Te6edf3validate_rnode_txpower
Te6edf3iface_type Tff7b72= Te6edf3ifaceTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3iface_typeTb4b4b4, Tffa657strTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72None
Tff7b72if Te6edf3iface_type Tff7b72in Tb4b4b4(Ta5d6ff"Ta5d6ffRNodeInterfaceTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffRNodeIPInterfaceTa5d6ff"Tb4b4b4)Tb4b4b4:
Te6edf3txpower Tff7b72= Te6edf3ifaceTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftxpowerTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3validate_rnode_txpowerTb4b4b4(Te6edf3txpowerTb4b4b4) Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72return Te6edf3txpower
Tff7b72return Tff7b72None
Tff7b72if Te6edf3iface_type Tff7b72!= Ta5d6ff"Ta5d6ffRNodeMultiInterfaceTa5d6ff"Tb4b4b4:
Tff7b72return Tff7b72None
Tff7b72for Te6edf3value Tff7b72in Te6edf3ifaceTff7b72.Td2a8ffvaluesTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3valueTb4b4b4, Tffa657dictTb4b4b4) Tff7b72and Ta5d6ff"Ta5d6fftxpowerTa5d6ff" Tff7b72in Te6edf3valueTb4b4b4:
Te6edf3txpower Tff7b72= Te6edf3valueTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftxpowerTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3validate_rnode_txpowerTb4b4b4(Te6edf3txpowerTb4b4b4) Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72return Te6edf3txpower
Te6edf3prefix Tff7b72= Ta5d6fffTa5d6ff"Tffd700{Te6edf3iface_nameTffd700}Ta5d6ff.Ta5d6ff"
Tff7b72for Te6edf3sub_nameTb4b4b4, Te6edf3sub_iface Tff7b72in Te6edf3interfacesTff7b72.Td2a8ffitemsTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72if Tff7b72not Te6edf3sub_nameTff7b72.Td2a8ffstartswithTb4b4b4(Te6edf3prefixTb4b4b4)Tb4b4b4:
Tff7b72continue
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3sub_ifaceTb4b4b4, Tffa657dictTb4b4b4)Tb4b4b4:
Tff7b72continue
Te6edf3txpower Tff7b72= Te6edf3sub_ifaceTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftxpowerTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3validate_rnode_txpowerTb4b4b4(Te6edf3txpowerTb4b4b4) Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72return Te6edf3txpower
Tff7b72return Tff7b72None
Tff7b72def Td2a8ff_rnode_interface_has_invalid_txpowerTb4b4b4(
Te6edf3iface_nameTb4b4b4: Tffa657strTb4b4b4,
Te6edf3ifaceTb4b4b4: Tffa657dictTb4b4b4,
Te6edf3interfacesTb4b4b4: Tffa657dictTb4b4b4,
Tb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
Tff7b72return Te6edf3_find_invalid_rnode_txpowerTb4b4b4(Te6edf3iface_nameTb4b4b4, Te6edf3ifaceTb4b4b4, Te6edf3interfacesTb4b4b4) Tff7b72is Tff7b72not Tff7b72None
Tff7b72def Td2a8ffguard_invalid_rnode_txpower_in_configTb4b4b4(Te6edf3config_pathTb4b4b4: Tffa657strTb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
T8b949e"""Disable RNode interfaces whose TX power would crash Reticulum on startup."""
Tff7b72import T7ee787os
Tff7b72if Tff7b72not Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffisfileTb4b4b4(Te6edf3config_pathTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72False
Tff7b72tryTb4b4b4:
Tff7b72from T7ee787RNST7ee787.T7ee787vendorT7ee787.T7ee787configobj Tff7b72import Te6edf3ConfigObj
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787interface_editor Tff7b72import Te6edf3validate_rnode_txpower
Te6edf3cfg Tff7b72= Te6edf3ConfigObjTb4b4b4(Te6edf3config_pathTb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72return Tff7b72False
Te6edf3modified Tff7b72= Tff7b72False
Te6edf3interfaces Tff7b72= Te6edf3cfgTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffinterfacesTa5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3interfacesTb4b4b4, Tffa657dictTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72False
Tff7b72for Te6edf3iface_nameTb4b4b4, Te6edf3iface Tff7b72in Te6edf3interfacesTff7b72.Td2a8ffitemsTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3ifaceTb4b4b4, Tffa657dictTb4b4b4)Tb4b4b4:
Tff7b72continue
Tff7b72if Tff7b72not Te6edf3_rnode_interface_has_invalid_txpowerTb4b4b4(Te6edf3iface_nameTb4b4b4, Te6edf3ifaceTb4b4b4, Te6edf3interfacesTb4b4b4)Tb4b4b4:
Tff7b72continue
Tff7b72if Tff7b72not Te6edf3_is_enabledTb4b4b4(Te6edf3ifaceTb4b4b4)Tb4b4b4:
Tff7b72continue
Te6edf3ifaceTb4b4b4[Ta5d6ff"Ta5d6ffinterface_enabledTa5d6ff"Tb4b4b4] Tff7b72= Ta5d6ff"Ta5d6fffalseTa5d6ff"
Te6edf3modified Tff7b72= Tff7b72True
Te6edf3txpower Tff7b72= Te6edf3_find_invalid_rnode_txpowerTb4b4b4(Te6edf3iface_nameTb4b4b4, Te6edf3ifaceTb4b4b4, Te6edf3interfacesTb4b4b4)
Te6edf3detail Tff7b72= Te6edf3validate_rnode_txpowerTb4b4b4(Te6edf3txpowerTb4b4b4) Tff7b72or Ta5d6ff"Ta5d6ffinvalid TX powerTa5d6ff"
Te6edf3loggerTff7b72.Td2a8ffwarningTb4b4b4(
Ta5d6ff'Ta5d6ffDisabled RNode interface Ta5d6ff"Tffd700%sTa5d6ff"Ta5d6ff before startup: Tffd700%sTa5d6ff'Tb4b4b4,
Te6edf3iface_nameTb4b4b4,
Te6edf3detailTb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3modifiedTb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3cfgTff7b72.Td2a8ffwriteTb4b4b4(Tb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72pass
Tff7b72return Te6edf3modified
Tff7b72def Td2a8ffguard_rnode_interfaces_on_androidTb4b4b4(Te6edf3config_pathTb4b4b4: Tffa657strTb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
T8b949e"""On Android, disable RNode interfaces that can't be brought up on this build.
RNode over TCP is unaffected. Serial, classic-Bluetooth, and BLE entries
are disabled only when their required native module isn't bundled, and
RNodeMultiInterface is always disabled since RNS has no Android-specific
implementation of it.
"""
Tff7b72if Tff7b72not Te6edf3_is_chaquopy_androidTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72False
Te6edf3disabled Tff7b72= Te6edf3disable_rnode_interfaces_in_configTb4b4b4(Te6edf3config_pathTb4b4b4, Te6edf3is_androidTff7b72=Tff7b72TrueTb4b4b4)
Tff7b72if Te6edf3disabledTb4b4b4:
Te6edf3loggerTff7b72.Td2a8ffwarningTb4b4b4(
Ta5d6ff"Ta5d6ffOne or more RNode interfaces were disabled because their transport Ta5d6ff"
Ta5d6ff"Ta5d6ffis not supported on this Android build. RNode over TCP is unaffected.Ta5d6ff"Tb4b4b4,
Tb4b4b4)
Tff7b72return Te6edf3disabled
Tff7b72def Td2a8ffguard_rnode_interfaces_on_desktopTb4b4b4(Te6edf3config_pathTb4b4b4: Tffa657strTb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
T8b949e"""On desktop, disable RNode interfaces that can't be brought up here.
RNode over TCP is unaffected. Serial and classic-Bluetooth need pyserial;
BLE needs bleak. Unsupported entries are disabled before Reticulum startup
so RNS does not crash with a missing-dependency error.
"""
Tff7b72if Te6edf3_is_chaquopy_androidTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72False
Te6edf3disabled Tff7b72= Te6edf3disable_rnode_interfaces_in_configTb4b4b4(Te6edf3config_pathTb4b4b4, Te6edf3is_androidTff7b72=Tff7b72FalseTb4b4b4)
Tff7b72if Te6edf3disabledTb4b4b4:
Te6edf3loggerTff7b72.Td2a8ffwarningTb4b4b4(
Ta5d6ff"Ta5d6ffOne or more RNode interfaces were disabled because their transport Ta5d6ff"
Ta5d6ff"Ta5d6ffis not supported on this build (serial/Bluetooth need pyserial; Ta5d6ff"
Ta5d6ff"Ta5d6ffBLE needs bleak). RNode over TCP is unaffected.Ta5d6ff"Tb4b4b4,
Tb4b4b4)
Tff7b72return Te6edf3disabled
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────